From: Carlos Soriano Date: Tue, 17 Jun 2014 10:47:00 +0000 (+0200) Subject: appDisplay: Animate folder view items X-Git-Tag: archive/raspbian/50.5-1+rpi1~1^2~1013^2^2~2211 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=9c474a635a207ad04fec778435edb4bf1db4b7a8;p=gnome-shell.git appDisplay: Animate folder view items Add a new animation to folder view based on designers mockups that emulates pulsating icons. The code on iconGrid is though to work well for the upcoming patches to animate AllView and FrequentView. https://bugzilla.gnome.org/show_bug.cgi?id=734726 --- diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index 0bf435e25..8b00cfeb7 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -964,6 +964,10 @@ const FolderView = new Lang.Class({ Util.ensureActorVisibleInScrollView(this.actor, actor); }, + animate: function(animationDirection) { + this._grid.animatePulse(animationDirection); + }, + createFolderIcon: function(size) { let layout = new Clutter.GridLayout(); let icon = new St.Widget({ layout_manager: layout, @@ -1339,8 +1343,17 @@ const AppFolderPopup = new Lang.Class({ this.actor.show(); this._boxPointer.setArrowActor(this._source.actor); + // We need to hide the icons of the view until the boxpointer animation + // is completed so we can animate the icons after as we like without + // showing them while boxpointer is animating. + this._view.actor.opacity = 0; this._boxPointer.show(BoxPointer.PopupAnimation.FADE | - BoxPointer.PopupAnimation.SLIDE); + BoxPointer.PopupAnimation.SLIDE, + Lang.bind(this, + function() { + this._view.actor.opacity = 255; + this._view.animate(IconGrid.AnimationDirection.IN); + })); this.emit('open-state-changed', true); }, diff --git a/js/ui/iconGrid.js b/js/ui/iconGrid.js index 4c555acce..5cd32c184 100644 --- a/js/ui/iconGrid.js +++ b/js/ui/iconGrid.js @@ -10,12 +10,22 @@ const St = imports.gi.St; const Lang = imports.lang; const Params = imports.misc.params; const Tweener = imports.ui.tweener; +const Main = imports.ui.main; const ICON_SIZE = 96; const MIN_ICON_SIZE = 16; const EXTRA_SPACE_ANIMATION_TIME = 0.25; +const ANIMATION_TIME_IN = 0.350; +const ANIMATION_MAX_DELAY_FOR_ITEM = 2/3 * ANIMATION_TIME_IN; + +const ANIMATION_BOUNCE_ICON_SCALE = 1.1; + +const AnimationDirection = { + IN: 0 +}; + const BaseIcon = new Lang.Class({ Name: 'BaseIcon', @@ -338,15 +348,91 @@ const IconGrid = new Lang.Class({ } }, - _calculateChildBox: function(child, x, y, box) { - let [childMinWidth, childMinHeight, childNaturalWidth, childNaturalHeight] = - child.get_preferred_size(); + /** + * Intended to be override by subclasses if they need a different + * set of items to be animated. + */ + _getChildrenToAnimate: function() { + return this._getVisibleChildren(); + }, + + _animationDone: function() { + this._animating = false; + this.emit('animation-done'); + }, + + animatePulse: function(animationDirection) { + if (animationDirection != AnimationDirection.IN) + throw new Error("Pulse animation only implements 'in' animation direction"); + + if (this._animating) + return; + + this._animating = true; + + let actors = this._getChildrenToAnimate(); + if (actors.length == 0) { + this._animationDone(); + return; + } + for (let index = 0; index < actors.length; index++) { + let actor = actors[index]; + actor.opacity = 0; + + let delay = index / actors.length * ANIMATION_MAX_DELAY_FOR_ITEM; + let [originalX, originalY] = actor.get_transformed_position(); + let [originalWidth, originalHeight,,] = this._getAllocatedChildSizeAndSpacing(actor); + + let actorClone = new Clutter.Clone({ source: actor }); + Main.uiGroup.add_actor(actorClone); + + actorClone.set_position(originalX, originalY); + actorClone.set_scale(0, 0); + actorClone.set_pivot_point(0.5, 0.5); + actorClone.set_size(originalWidth, originalHeight); + + let bounceUpTime = ANIMATION_TIME_IN / 4; + // Defeat onComplete anonymous function closure + let isLastItem = index == actors.length - 1; + Tweener.addTween(actorClone, + { time: bounceUpTime, + transition: 'easeInOutQuad', + delay: delay, + scale_x: ANIMATION_BOUNCE_ICON_SCALE, + scale_y: ANIMATION_BOUNCE_ICON_SCALE, + onComplete: Lang.bind(this, function() { + Tweener.addTween(actorClone, + { time: ANIMATION_TIME_IN - bounceUpTime, + transition: 'easeInOutQuad', + scale_x: 1, + scale_y: 1, + onComplete: Lang.bind(this, function() { + if (isLastItem) + this._animationDone(); + + actor.opacity = 255; + actorClone.destroy(); + }) + }); + }) + }); + } + }, + + _getAllocatedChildSizeAndSpacing: function(child) { + let [,, natWidth, natHeight] = child.get_preferred_size(); + let width = Math.min(this._getHItemSize(), natWidth); + let xSpacing = Math.max(0, width - natWidth) / 2; + let height = Math.min(this._getVItemSize(), natHeight); + let ySpacing = Math.max(0, height - natHeight) / 2; + return [width, height, xSpacing, ySpacing]; + }, + + _calculateChildBox: function(child, x, y, box) { /* Center the item in its allocation horizontally */ - let width = Math.min(this._getHItemSize(), childNaturalWidth); - let childXSpacing = Math.max(0, width - childNaturalWidth) / 2; - let height = Math.min(this._getVItemSize(), childNaturalHeight); - let childYSpacing = Math.max(0, height - childNaturalHeight) / 2; + let [width, height, childXSpacing, childYSpacing] = + this._getAllocatedChildSizeAndSpacing(child); let childBox = new Clutter.ActorBox(); if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {